{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# The generic Broker class"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from abc import abstractmethod\n",
    "\n",
    "\n",
    "class Broker(object):\n",
    "\tdef __init__(self, host, port):\n",
    "\t\tself.host = host\n",
    "\t\tself.port = port\n",
    "\n",
    "\t\tself.__price_event_handler = None\n",
    "\t\tself.__order_event_handler = None\n",
    "\t\tself.__position_event_handler = None\n",
    "\n",
    "\t@property\n",
    "\tdef on_price_event(self):\n",
    "\t\t\"\"\"\n",
    "\t\tListeners will receive:\n",
    "\t\tsymbol, bid, ask\n",
    "\t\t\"\"\"\n",
    "\t\treturn self.__price_event_handler\n",
    "\n",
    "\t@on_price_event.setter\n",
    "\tdef on_price_event(self, event_handler):\n",
    "\t\tself.__price_event_handler = event_handler\n",
    "\n",
    "\t@property\n",
    "\tdef on_order_event(self):\n",
    "\t\t\"\"\"\n",
    "\t\tListeners will receive:\n",
    "\t\ttransaction_id\n",
    "\t\t\"\"\"\n",
    "\t\treturn self.__order_event_handler\n",
    "\n",
    "\t@on_order_event.setter\n",
    "\tdef on_order_event(self, event_handler):\n",
    "\t\tself.__order_event_handler = event_handler\n",
    "\n",
    "\t@property\n",
    "\tdef on_position_event(self):\n",
    "\t\t\"\"\"\n",
    "\t\tListeners will receive:\n",
    "\t\tsymbol, is_long, units, unrealized_pnl, pnl\n",
    "\t\t\"\"\"\n",
    "\t\treturn self.__position_event_handler\n",
    "\n",
    "\t@on_position_event.setter\n",
    "\tdef on_position_event(self, event_handler):\n",
    "\t\tself.__position_event_handler = event_handler\n",
    "\n",
    "\t@abstractmethod\n",
    "\tdef get_prices(self, symbols=[]):\n",
    "\t\t\"\"\"\n",
    "\t\tQuery market prices from a broker\n",
    "\t\t:param symbols: list of symbols recognized by your broker\n",
    "\t\t\"\"\"\n",
    "\t\traise NotImplementedError('Method is required!')\n",
    "\n",
    "\t@abstractmethod\n",
    "\tdef stream_prices(self, symbols=[]):\n",
    "\t\t\"\"\"\"\n",
    "\t\tContinuously stream prices from a broker.\n",
    "\t\t:param symbols: list of symbols recognized by your broker\n",
    "\t\t\"\"\"\n",
    "\t\traise NotImplementedError('Method is required!')\n",
    "\n",
    "\t@abstractmethod\n",
    "\tdef send_market_order(self, symbol, quantity, is_buy):\n",
    "\t\traise NotImplementedError('Method is required!')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Oanda Broker class"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import v20\n",
    "\n",
    "\n",
    "class OandaBroker(Broker):\n",
    "\tPRACTICE_API_HOST = 'api-fxpractice.oanda.com'\n",
    "\tPRACTICE_STREAM_HOST = 'stream-fxpractice.oanda.com'\n",
    "\n",
    "\tLIVE_API_HOST = 'api-fxtrade.oanda.com'\n",
    "\tLIVE_STREAM_HOST = 'stream-fxtrade.oanda.com'\n",
    "\n",
    "\tPORT = '443'\n",
    "\n",
    "\tdef __init__(self, accountid, token, is_live=False):\n",
    "\t\tif is_live:\n",
    "\t\t\thost = self.LIVE_API_HOST\n",
    "\t\t\tstream_host = self.LIVE_STREAM_HOST\n",
    "\t\telse:\n",
    "\t\t\thost = self.PRACTICE_API_HOST\n",
    "\t\t\tstream_host = self.PRACTICE_STREAM_HOST\n",
    "\n",
    "\t\tsuper(OandaBroker, self).__init__(host, self.PORT)\n",
    "\n",
    "\t\tself.accountid = accountid\n",
    "\t\tself.token = token\n",
    "\n",
    "\t\tself.api = v20.Context(host, self.port, token=token)\n",
    "\t\tself.stream_api = v20.Context(stream_host, self.port, token=token)\n",
    "\n",
    "\tdef get_prices(self, symbols=[]):\n",
    "\t\tresponse = self.api.pricing.get(\n",
    "\t\t\tself.accountid,\n",
    "\t\t\tinstruments=\",\".join(symbols),\n",
    "\t\t\tsnapshot=True,\n",
    "\t\t\tincludeUnitsAvailable=False\n",
    "\t\t)\n",
    "\t\tbody = response.body\n",
    "\t\tprices = body.get('prices', [])\n",
    "\t\tfor price in prices:\n",
    "\t\t\tself.process_price(price)\n",
    "\n",
    "\tdef process_price(self, price):\n",
    "\t\tsymbol = price.instrument\n",
    "\n",
    "\t\tif not symbol:\n",
    "\t\t\tprint('Price symbol is empty!')\n",
    "\t\t\treturn\n",
    "\n",
    "\t\tbids = price.bids or []\n",
    "\t\tprice_bucket_bid = bids[0] if bids and len(bids) > 0 else None\n",
    "\t\tbid = price_bucket_bid.price if price_bucket_bid else 0\n",
    "\n",
    "\t\tasks = price.asks or []\n",
    "\t\tprice_bucket_ask = asks[0] if asks and len(asks) > 0 else None\n",
    "\t\task = price_bucket_ask.price if price_bucket_ask else 0\n",
    "\n",
    "\t\tself.on_price_event(symbol, bid, ask)\n",
    "\n",
    "\tdef stream_prices(self, symbols=[]):\n",
    "\t\tresponse = self.stream_api.pricing.stream(\n",
    "\t\t\tself.accountid,\n",
    "\t\t\tinstruments=\",\".join(symbols),\n",
    "\t\t\tsnapshot=True\n",
    "\t\t)\n",
    "\n",
    "\t\tfor msg_type, msg in response.parts():\n",
    "\t\t\tif msg_type == \"pricing.Heartbeat\":\n",
    "\t\t\t\tcontinue\n",
    "\t\t\telif msg_type == \"pricing.ClientPrice\":\n",
    "\t\t\t\tself.process_price(msg)\n",
    "\n",
    "\tdef send_market_order(self, symbol, quantity, is_buy):\n",
    "\t\tresponse = self.api.order.market(\n",
    "\t\t\tself.accountid,\n",
    "\t\t\tunits=abs(quantity) * (1 if is_buy else -1),\n",
    "\t\t\tinstrument=symbol,\n",
    "\t\t\ttype='MARKET',\n",
    "\t\t)\n",
    "\t\tif response.status != 201:\n",
    "\t\t\tself.on_order_event(symbol, quantity, is_buy, None, 'NOT_FILLED')\n",
    "\t\t\treturn\n",
    "\n",
    "\t\tbody = response.body\n",
    "\t\tif 'orderCancelTransaction' in body:\n",
    "\t\t\tself.on_order_event(symbol, quantity, is_buy, None, 'NOT_FILLED')\n",
    "\t\t\treturn\n",
    "        \n",
    "\t\ttransaction_id = body.get('lastTransactionID', None)\n",
    "\t\tself.on_order_event(symbol, quantity, is_buy, transaction_id, 'FILLED')\n",
    "\n",
    "\tdef get_positions(self):\n",
    "\t\tresponse = self.api.position.list(self.accountid)\n",
    "\t\tbody = response.body\n",
    "\t\tpositions = body.get('positions', [])\n",
    "\t\tfor position in positions:\n",
    "\t\t\tsymbol = position.instrument\n",
    "\t\t\tunrealized_pnl = position.unrealizedPL\n",
    "\t\t\tpnl = position.pl\n",
    "\t\t\tlong = position.long\n",
    "\t\t\tshort = position.short\n",
    "\n",
    "\t\t\tif short.units:\n",
    "\t\t\t\tself.on_position_event(\n",
    "\t\t\t\t\tsymbol, False, short.units, unrealized_pnl, pnl)\n",
    "\t\t\telif long.units:\n",
    "\t\t\t\tself.on_position_event(\n",
    "\t\t\t\t\tsymbol, True, long.units, unrealized_pnl, pnl)\n",
    "\t\t\telse:\n",
    "\t\t\t\tself.on_position_event(\n",
    "\t\t\t\t\tsymbol, None, 0, unrealized_pnl, pnl)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Getting prices"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Replace these 2 values with your own!\n",
    "ACCOUNT_ID = '101-001-1374173-001'\n",
    "API_TOKEN = '6ecf6b053262c590b78bb8199b85aa2f-d99c54aecb2d5b4583a9f707636e8009'\n",
    "\n",
    "broker = OandaBroker(ACCOUNT_ID, API_TOKEN)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "SYMBOL = 'EUR_USD'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "import datetime as dt\n",
    "\n",
    "def on_price_event(symbol, bid, ask):\n",
    "    print(\n",
    "        dt.datetime.now(), '[PRICE]',\n",
    "        symbol, 'bid:', bid, 'ask:', ask\n",
    "    )\n",
    "\n",
    "broker.on_price_event = on_price_event"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2019-04-25 20:00:06.211394 [PRICE] EUR_USD bid: 1.11236 ask: 1.1125\n"
     ]
    }
   ],
   "source": [
    "broker.get_prices(symbols=[SYMBOL])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Sending a simple market order"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2019-04-25 19:45:09.850931 [ORDER] transaction_id: 3105 status: FILLED symbol: EUR_USD quantity: 1 is_buy: True\n"
     ]
    }
   ],
   "source": [
    "def on_order_event(symbol, quantity, is_buy, transaction_id, status):\n",
    "    print(\n",
    "        dt.datetime.now(), '[ORDER]',\n",
    "        'transaction_id:', transaction_id,\n",
    "        'status:', status,\n",
    "        'symbol:', symbol,\n",
    "        'quantity:', quantity,\n",
    "        'is_buy:', is_buy,\n",
    "    )\n",
    "\n",
    "broker.on_order_event = on_order_event\n",
    "broker.send_market_order(SYMBOL, 1, True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Getting position updates"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2019-04-25 19:45:11.169091 [POSITION] symbol: USD_CAD is_long: None units: 0 upnl: 0.0 pnl: -0.0002\n",
      "2019-04-25 19:45:11.170083 [POSITION] symbol: EUR_USD is_long: None units: 0 upnl: 0.0 pnl: -0.3905\n"
     ]
    }
   ],
   "source": [
    "def on_position_event(symbol, is_long, units, upnl, pnl):\n",
    "    print(\n",
    "        dt.datetime.now(), '[POSITION]',\n",
    "        'symbol:', symbol,\n",
    "        'is_long:', is_long,\n",
    "        'units:', units,\n",
    "        'upnl:', upnl,\n",
    "        'pnl:', pnl\n",
    "    )\n",
    "\n",
    "broker.on_position_event = on_position_event\n",
    "broker.get_positions()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Building a mean-reverting algorithmic trading system"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "import datetime as dt\n",
    "import pandas as pd\n",
    "\n",
    "\n",
    "class MeanReversionTrader(object):\n",
    "    def __init__(\n",
    "        self, broker, symbol=None, units=1,\n",
    "        resample_interval='60s', mean_periods=5\n",
    "    ):\n",
    "        \"\"\"\n",
    "        A trading platform that trades on one side\n",
    "            based on a mean-reverting algorithm.\n",
    "\n",
    "        :param broker: Broker object\n",
    "        :param symbol: A str object recognized by the broker for trading\n",
    "        :param units: Number of units to trade\n",
    "        :param resample_interval:\n",
    "            Frequency for resampling price time series\n",
    "        :param mean_periods: Number of resampled intervals\n",
    "            for calculating the average price\n",
    "        \"\"\"\n",
    "        self.broker = self.setup_broker(broker)\n",
    "\n",
    "        self.resample_interval = resample_interval\n",
    "        self.mean_periods = mean_periods\n",
    "        self.symbol = symbol\n",
    "        self.units = units\n",
    "\n",
    "        self.df_prices = pd.DataFrame(columns=[symbol])\n",
    "        self.pnl, self.upnl = 0, 0\n",
    "\n",
    "        self.bid_price, self.ask_price = 0, 0\n",
    "        self.position = 0\n",
    "        self.is_order_pending = False\n",
    "        self.is_next_signal_cycle = True\n",
    "\n",
    "    def setup_broker(self, broker):\n",
    "        broker.on_price_event = self.on_price_event\n",
    "        broker.on_order_event = self.on_order_event\n",
    "        broker.on_position_event = self.on_position_event\n",
    "        return broker\n",
    "\n",
    "    def on_price_event(self, symbol, bid, ask):\n",
    "        print(dt.datetime.now(), '[PRICE]', symbol, 'bid:', bid, 'ask:', ask)\n",
    "\n",
    "        self.bid_price = bid\n",
    "        self.ask_price = ask\n",
    "        self.df_prices.loc[pd.Timestamp.now(), symbol] = (bid + ask) / 2.\n",
    "\n",
    "        self.get_positions()\n",
    "        self.generate_signals_and_think()\n",
    "\n",
    "        self.print_state()\n",
    "\n",
    "    def get_positions(self):\n",
    "        try:\n",
    "            self.broker.get_positions()\n",
    "        except Exception as ex:\n",
    "            print('get_positions error:', ex)\n",
    "\n",
    "    def on_order_event(self, symbol, quantity, is_buy, transaction_id, status):\n",
    "        print(\n",
    "            dt.datetime.now(), '[ORDER]',\n",
    "            'transaction_id:', transaction_id,\n",
    "            'status:', status,\n",
    "            'symbol:', symbol,\n",
    "            'quantity:', quantity,\n",
    "            'is_buy:', is_buy,\n",
    "        )\n",
    "        if status == 'FILLED':\n",
    "            self.is_order_pending = False\n",
    "            self.is_next_signal_cycle = False\n",
    "\n",
    "            self.get_positions()  # Update positions before thinking\n",
    "            self.generate_signals_and_think()\n",
    "\n",
    "    def on_position_event(self, symbol, is_long, units, upnl, pnl):\n",
    "        if symbol == self.symbol:\n",
    "            self.position = abs(units) * (1 if is_long else -1)\n",
    "            self.pnl = pnl\n",
    "            self.upnl = upnl\n",
    "            self.print_state()\n",
    "\n",
    "    def print_state(self):\n",
    "        print(\n",
    "            dt.datetime.now(), self.symbol, self.position_state,\n",
    "            abs(self.position), 'upnl:', self.upnl, 'pnl:', self.pnl\n",
    "        )\n",
    "\n",
    "    @property\n",
    "    def position_state(self):\n",
    "        if self.position == 0:\n",
    "            return 'FLAT'\n",
    "        if self.position > 0:\n",
    "            return 'LONG'\n",
    "        if self.position < 0:\n",
    "            return 'SHORT'\n",
    "\n",
    "    def generate_signals_and_think(self):\n",
    "        df_resampled = self.df_prices\\\n",
    "            .resample(self.resample_interval)\\\n",
    "            .ffill()\\\n",
    "            .dropna()\n",
    "        resampled_len = len(df_resampled.index)\n",
    "\n",
    "        if resampled_len < self.mean_periods:\n",
    "            print(\n",
    "                'Insufficient data size to calculate logic. Need',\n",
    "                self.mean_periods - resampled_len, 'more.'\n",
    "            )\n",
    "            return\n",
    "\n",
    "        mean = df_resampled.tail(self.mean_periods).mean()[self.symbol]\n",
    "\n",
    "        # Signal flag calculation\n",
    "        is_signal_buy = mean > self.ask_price\n",
    "        is_signal_sell = mean < self.bid_price\n",
    "\n",
    "        print(\n",
    "            'is_signal_buy:', is_signal_buy,\n",
    "            'is_signal_sell:', is_signal_sell,\n",
    "            'average_price: %.5f' % mean,\n",
    "            'bid:', self.bid_price,\n",
    "            'ask:', self.ask_price\n",
    "        )\n",
    "\n",
    "        self.think(is_signal_buy, is_signal_sell)\n",
    "\n",
    "    def think(self, is_signal_buy, is_signal_sell):\n",
    "        if self.is_order_pending:\n",
    "            return\n",
    "\n",
    "        if self.position == 0:\n",
    "            self.think_when_position_flat(is_signal_buy, is_signal_sell)\n",
    "        elif self.position > 0:\n",
    "            self.think_when_position_long(is_signal_sell)\n",
    "        elif self.position < 0: \n",
    "            self.think_when_position_short(is_signal_buy)        \n",
    "\n",
    "    def think_when_position_flat(self, is_signal_buy, is_signal_sell):\n",
    "        if is_signal_buy and self.is_next_signal_cycle:\n",
    "            print('Opening position, BUY', \n",
    "                  self.symbol, self.units, 'units')\n",
    "            self.is_order_pending = True\n",
    "            self.send_market_order(self.symbol, self.units, True)\n",
    "            return\n",
    "\n",
    "        if is_signal_sell and self.is_next_signal_cycle:\n",
    "            print('Opening position, SELL', \n",
    "                  self.symbol, self.units, 'units')\n",
    "            self.is_order_pending = True\n",
    "            self.send_market_order(self.symbol, self.units, False)\n",
    "            return\n",
    "\n",
    "        if not is_signal_buy and not is_signal_sell:\n",
    "            self.is_next_signal_cycle = True\n",
    "\n",
    "    def think_when_position_long(self, is_signal_sell):\n",
    "        if is_signal_sell:\n",
    "            print('Closing position, SELL', \n",
    "                  self.symbol, self.units, 'units')\n",
    "            self.is_order_pending = True\n",
    "            self.send_market_order(self.symbol, self.units, False)\n",
    "\n",
    "    def think_when_position_short(self, is_signal_buy):\n",
    "        if is_signal_buy:\n",
    "            print('Closing position, BUY', \n",
    "                  self.symbol, self.units, 'units')\n",
    "            self.is_order_pending = True\n",
    "            self.send_market_order(self.symbol, self.units, True)\n",
    "\n",
    "    def send_market_order(self, symbol, quantity, is_buy):\n",
    "        self.broker.send_market_order(symbol, quantity, is_buy)\n",
    "\n",
    "    def run(self):\n",
    "        self.broker.stream_prices(symbols=[self.symbol])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "WARNING! Running the codes below will block on the main thread! You will have to restart the kernel."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2019-04-25 19:58:38.364680 [PRICE] EUR_USD bid: 1.11222 ask: 1.11234\n",
      "2019-04-25 19:58:38.620637 EUR_USD LONG 1.0 upnl: -0.0002 pnl: -0.3906\n",
      "Insufficient data size to calculate logic. Need 5 more.\n",
      "2019-04-25 19:58:38.630637 EUR_USD LONG 1.0 upnl: -0.0002 pnl: -0.3906\n",
      "2019-04-25 19:58:52.635136 [PRICE] EUR_USD bid: 1.1122 ask: 1.11232\n",
      "2019-04-25 19:58:52.884808 EUR_USD LONG 1.0 upnl: -0.0002 pnl: -0.3906\n",
      "Insufficient data size to calculate logic. Need 5 more.\n",
      "2019-04-25 19:58:52.888816 EUR_USD LONG 1.0 upnl: -0.0002 pnl: -0.3906\n",
      "2019-04-25 19:58:58.140804 [PRICE] EUR_USD bid: 1.11217 ask: 1.11231\n",
      "2019-04-25 19:58:58.391596 EUR_USD LONG 1.0 upnl: -0.0002 pnl: -0.3906\n",
      "Insufficient data size to calculate logic. Need 5 more.\n",
      "2019-04-25 19:58:58.395609 EUR_USD LONG 1.0 upnl: -0.0002 pnl: -0.3906\n",
      "2019-04-25 19:58:58.889629 [PRICE] EUR_USD bid: 1.1122 ask: 1.11232\n",
      "2019-04-25 19:58:59.138592 EUR_USD LONG 1.0 upnl: -0.0002 pnl: -0.3906\n",
      "Insufficient data size to calculate logic. Need 5 more.\n",
      "2019-04-25 19:58:59.143592 EUR_USD LONG 1.0 upnl: -0.0002 pnl: -0.3906\n",
      "2019-04-25 19:59:02.641582 [PRICE] EUR_USD bid: 1.11222 ask: 1.11234\n",
      "2019-04-25 19:59:02.891546 EUR_USD LONG 1.0 upnl: -0.0002 pnl: -0.3906\n",
      "Insufficient data size to calculate logic. Need 4 more.\n",
      "2019-04-25 19:59:02.895546 EUR_USD LONG 1.0 upnl: -0.0002 pnl: -0.3906\n",
      "2019-04-25 19:59:09.658266 [PRICE] EUR_USD bid: 1.11225 ask: 1.11237\n",
      "2019-04-25 19:59:09.907490 EUR_USD LONG 1.0 upnl: -0.0001 pnl: -0.3906\n",
      "Insufficient data size to calculate logic. Need 4 more.\n",
      "2019-04-25 19:59:09.911491 EUR_USD LONG 1.0 upnl: -0.0001 pnl: -0.3906\n",
      "2019-04-25 19:59:12.411634 [PRICE] EUR_USD bid: 1.11228 ask: 1.11239\n",
      "2019-04-25 19:59:12.662153 EUR_USD LONG 1.0 upnl: -0.0001 pnl: -0.3906\n",
      "Insufficient data size to calculate logic. Need 4 more.\n",
      "2019-04-25 19:59:12.666153 EUR_USD LONG 1.0 upnl: -0.0001 pnl: -0.3906\n",
      "2019-04-25 19:59:57.472590 [PRICE] EUR_USD bid: 1.11232 ask: 1.11243\n",
      "2019-04-25 19:59:57.722591 EUR_USD LONG 1.0 upnl: 0.0 pnl: -0.3906\n",
      "Insufficient data size to calculate logic. Need 4 more.\n",
      "2019-04-25 19:59:57.726591 EUR_USD LONG 1.0 upnl: 0.0 pnl: -0.3906\n"
     ]
    }
   ],
   "source": [
    "trader = MeanReversionTrader(\n",
    "    broker,\n",
    "    resample_interval='60s',\n",
    "    symbol='EUR_USD',\n",
    "    units=1\n",
    ")\n",
    "trader.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Building a trend-following trading platform"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "class TrendFollowingTrader(MeanReversionTrader):\n",
    "\tdef __init__(\n",
    "\t\tself, *args, long_mean_periods=10,\n",
    "\t\tbuy_threshold=1.0, sell_threshold=1.0, **kwargs\n",
    "\t):\n",
    "\t\tsuper(TrendFollowingTrader, self).__init__(*args, **kwargs)\n",
    "\n",
    "\t\tself.long_mean_periods = long_mean_periods\n",
    "\t\tself.buy_threshold = buy_threshold\n",
    "\t\tself.sell_threshold = sell_threshold\n",
    "\n",
    "\tdef generate_signals_and_think(self):\n",
    "\t\tdf_resampled = self.df_prices\\\n",
    "\t\t\t.resample(self.resample_interval)\\\n",
    "\t\t\t.ffill().dropna()\n",
    "\t\tresampled_len = len(df_resampled.index)\n",
    "\n",
    "\t\tif resampled_len < self.long_mean_periods:\n",
    "\t\t\tprint(\n",
    "\t\t\t\t'Insufficient data size to calculate logic. Need',\n",
    "\t\t\t\tself.mean_periods - resampled_len, 'more.'\n",
    "\t\t\t)\n",
    "\t\t\treturn\n",
    "\n",
    "\t\tmean_short = df_resampled\\\n",
    "\t\t\t.tail(self.mean_periods).mean()[self.symbol]\n",
    "\t\tmean_long = df_resampled\\\n",
    "\t\t\t.tail(self.long_mean_periods).mean()[self.symbol]\n",
    "\t\tbeta = mean_short / mean_long\n",
    "\n",
    "\t\t# Signal flag calculation\n",
    "\t\tis_signal_buy = beta > self.buy_threshold\n",
    "\t\tis_signal_sell = beta < self.sell_threshold\n",
    "\n",
    "\t\tprint(\n",
    "\t\t\t'is_signal_buy:', is_signal_buy,\n",
    "\t\t\t'is_signal_sell:', is_signal_sell,\n",
    "\t\t\t'beta:', beta,\n",
    "\t\t\t'bid:', self.bid_price,\n",
    "\t\t\t'ask:', self.ask_price\n",
    "\t\t)\n",
    "\n",
    "\t\tself.think(is_signal_buy, is_signal_sell)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "WARNING! Running the codes below will block on the main thread! You will have to restart the kernel."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2019-04-25 20:00:17.086038 [PRICE] EUR_USD bid: 1.11251 ask: 1.11264\n",
      "2019-04-25 20:00:17.339036 EUR_USD LONG 1.0 upnl: 0.0001 pnl: -0.3906\n",
      "Insufficient data size to calculate logic. Need 5 more.\n",
      "2019-04-25 20:00:17.350038 EUR_USD LONG 1.0 upnl: 0.0001 pnl: -0.3906\n",
      "2019-04-25 20:00:20.084558 [PRICE] EUR_USD bid: 1.11249 ask: 1.11261\n",
      "2019-04-25 20:00:20.334595 EUR_USD LONG 1.0 upnl: 0.0001 pnl: -0.3906\n",
      "Insufficient data size to calculate logic. Need 5 more.\n",
      "2019-04-25 20:00:20.338621 EUR_USD LONG 1.0 upnl: 0.0001 pnl: -0.3906\n",
      "2019-04-25 20:00:22.840495 [PRICE] EUR_USD bid: 1.11252 ask: 1.11264\n",
      "2019-04-25 20:00:23.089454 EUR_USD LONG 1.0 upnl: 0.0002 pnl: -0.3906\n",
      "Insufficient data size to calculate logic. Need 5 more.\n",
      "2019-04-25 20:00:23.093490 EUR_USD LONG 1.0 upnl: 0.0002 pnl: -0.3906\n"
     ]
    }
   ],
   "source": [
    "trader = TrendFollowingTrader(\n",
    "    broker,\n",
    "    resample_interval='60s',\n",
    "    symbol='EUR_USD',\n",
    "    units=1,\n",
    "    mean_periods=5,\n",
    "    long_mean_periods=10,\n",
    "    buy_threshold=1.000010,\n",
    "    sell_threshold=0.99990,\n",
    ")\n",
    "trader.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# VaR for risk management"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Download the all-time AAPL dataset\n",
    "\"\"\"\n",
    "from alpha_vantage.timeseries import TimeSeries\n",
    " \n",
    "# Update your Alpha Vantage API key here...\n",
    "ALPHA_VANTAGE_API_KEY = 'PZ2ISG9CYY379KLI'\n",
    " \n",
    "ts = TimeSeries(key=ALPHA_VANTAGE_API_KEY, output_format='pandas')\n",
    "df, meta_data = ts.get_daily_adjusted(symbol='AAPL', outputsize='full')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.info()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import datetime as dt\n",
    "import pandas as pd\n",
    " \n",
    "# Define the date range\n",
    "start = dt.datetime(2017, 1, 1)\n",
    "end = dt.datetime(2017, 12, 31)\n",
    " \n",
    "# Cast indexes as DateTimeIndex objects\n",
    "df.index = pd.to_datetime(df.index)\n",
    "closing_prices = df['5. adjusted close']\n",
    "prices = closing_prices.loc[start:end]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from scipy.stats import norm\n",
    "\n",
    "def calculate_daily_var(\n",
    "    portfolio, prob, mean, \n",
    "    stdev, days_per_year=252.\n",
    "):\n",
    "    alpha = 1-prob\n",
    "    u = mean/days_per_year\n",
    "    sigma = stdev/np.sqrt(days_per_year)\n",
    "    norminv = norm.ppf(alpha, u, sigma)\n",
    "    return portfolio - portfolio*(norminv+1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "portfolio = 100000000.00\n",
    "confidence = 0.95\n",
    " \n",
    "daily_returns = prices.pct_change().dropna()\n",
    "mu = np.mean(daily_returns)\n",
    "sigma = np.std(daily_returns)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Value-at-Risk: 114249.09\n"
     ]
    }
   ],
   "source": [
    "VaR = calculate_daily_var(\n",
    "    portfolio, confidence, mu, sigma, days_per_year=252.)\n",
    "print('Value-at-Risk: %.2f' % VaR)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
